package com.lwc.cfdns.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lwc.cfdns.common.Assert;
import com.lwc.cfdns.config.AppConfig;
import com.lwc.cfdns.config.JwtConfig;
import com.lwc.cfdns.constants.Constants;
import com.lwc.cfdns.constants.ConstantsUrl;
import com.lwc.cfdns.enums.ResultCode;
import com.lwc.cfdns.exception.BadException;
import com.lwc.cfdns.pojo.bo.system.AdminBO;
import com.lwc.cfdns.pojo.dto.ChangeStatusDTO;
import com.lwc.cfdns.pojo.dto.system.AdminDTO;
import com.lwc.cfdns.pojo.dto.system.AdminLoginDTO;
import com.lwc.cfdns.pojo.dto.system.AdminPwdDTO;
import com.lwc.cfdns.pojo.entity.SystemAdmin;
import com.lwc.cfdns.pojo.security.SecuritySystemAdminUserDetails;
import com.lwc.cfdns.pojo.vo.system.AdminLoginVO;
import com.lwc.cfdns.pojo.vo.system.AdminVO;
import com.lwc.cfdns.service.SystemAdminService;
import com.lwc.cfdns.mapper.SystemAdminMapper;
import com.lwc.cfdns.utils.*;
import com.lwc.cfdns.utils.ip.IpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author spring
 * @description 针对表【cfdns_system_admin(用户信息表)】的数据库操作Service实现
 * @createDate 2023-05-27 22:40:28
 */
@Service
public class SystemAdminServiceImpl extends ServiceImpl<SystemAdminMapper, SystemAdmin>
        implements SystemAdminService {
    private static final Logger logger = LoggerFactory.getLogger(SystemAdminServiceImpl.class);
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private JwtConfig jwtConfig;
    @Autowired
    private AuthenticationManager authenticationManager;

    // 使用security密码生成器
    @Resource
    private PasswordEncoder passwordEncoder;
    @Autowired
    private SystemAdminRoleServiceImpl systemAdminRoleService;
    @Autowired
    private SystemAdminDeptServiceImpl systemAdminDeptService;
    @Autowired
    private SystemAdminStationServiceImpl systemAdminStationService;
    @Autowired
    private SystemLogininforServiceImpl systemLogininforService;

    @Autowired
    private SystemConfigServiceImpl systemConfigService;

    /**
     * 登录前置检查
     *
     * @param userName
     * @param passWord
     * @return void
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/7/25 14:32:48
     */
    public void preCheck(String userName, String passWord) {
        if (StringUtils.isEmpty(userName) || StringUtils.isEmpty(passWord)) {
            throw new BadException("账号或密码为空");
        }
        logger.info("IP黑名单");
        String backIps = systemConfigService.viewByEnNameOfCache("sys.blackIPList").getValue();
        String ipAddr = IpUtils.getIpAddr();
        // IP黑名单校验
        if (StringUtils.isNotEmpty(backIps) && IpUtils.isMatchedIp(backIps, ipAddr)) {
            systemLogininforService.create(userName, "1", "登录IP被限制");
            throw new BadException("登录IP被限制");
        }
        Long errorNum = ConvertUtils.toLong(redisUtils.get(getUserErrorPwdKey(userName)), 0l);
        Long setErrorNum = ConvertUtils.toLong(AppConfig.getUsers().getLoginPwdNum(), 5l);
        logger.info("登录错误次数：{},限制次数:{}", errorNum, setErrorNum);
        if (errorNum > 0L && errorNum >= setErrorNum) {
            systemLogininforService.create(userName, "1", "登录密码错误次数超限制");
            throw new BadException("登录密码错误次数超限制,请稍后重试");
        }
    }

    /**
     * 记录密码登录错误次数的键名
     *
     * @param userName
     * @return String
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/7/25 14:41:37
     */
    public String getUserErrorPwdKey(String userName) {
        return Constants.adminLoginPwdError + userName;
    }

    /**
     * 删除登录时，密码错误次数超限锁定
     *
     * @param userName
     * @return void
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/7/26 17:06:10
     */
    public Boolean clearLoginUnlock(String userName) {
        Boolean del = redisUtils.del(getUserErrorPwdKey(userName));
        return del;
    }

    /**
     * 登录
     *
     * @param adminLoginDTO
     * @return AdminLoginVO
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/23 13:46:14
     */
    @Override
    public AdminLoginVO login(AdminLoginDTO adminLoginDTO) {
        Authentication authenticate = null;
        // 前置检查
        preCheck(adminLoginDTO.getUsername(), adminLoginDTO.getPassword());
        try {
            // ----通过secuity授权认证
            // 使用AuthenticationManager.authenticate进行用户认证 ---> 会去调用我们的SystemAdminDetailServiceImpl的loadUserByUsername进行认证
            authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(adminLoginDTO.getUsername(), adminLoginDTO.getPassword()));
        } catch (Exception e) {
            logger.info("登录错误了,{}", e.toString());
            if (e instanceof BadCredentialsException) {
                // 密码错误
                systemLogininforService.create(adminLoginDTO.getUsername(), "1", "登录密码错误");
                // 记录错误次数
                Long incr = redisUtils.incr(getUserErrorPwdKey(adminLoginDTO.getUsername()), 1l);
                redisUtils.expire(getUserErrorPwdKey(adminLoginDTO.getUsername()), ConvertUtils.toLong(AppConfig.getUsers().getLoginLockTime(), 600l));
                throw new BadException(ResultCode.AUTH_PWD_ERROR);
            } else {
                // 如果认证没通过，给出提示
                systemLogininforService.create(adminLoginDTO.getUsername(), "1", "账号或密码错误");
                throw new BadException(ResultCode.AUTH_PWD_LOGIN);
            }
        }
        systemLogininforService.create(adminLoginDTO.getUsername(), "0", "登录成功");
        redisUtils.del(getUserErrorPwdKey(adminLoginDTO.getUsername()));

        SecuritySystemAdminUserDetails securitySystemAdminUserDetails = (SecuritySystemAdminUserDetails) authenticate.getPrincipal();
        SystemAdmin admin = securitySystemAdminUserDetails.getSystemAdmin();

        // ----自己验证账号密码授权
//        SystemAdmin admin = this.baseMapper.selectOne(
//                new LambdaQueryWrapper<SystemAdmin>()
//                        .eq(SystemAdmin::getAccount, adminDTO.getAccount())
////                        .eq(SystemAdmin::getPassword, adminDTO.getPassword())
//                        .eq(SystemAdmin::getStatus, 10));
//        if (admin == null) {
//            throw new AuthException("账号不存在或已被禁用!");
//        }
//        // 验证密码
//        if (!this.validatePassword(adminDTO.getPassword(), admin.getPassword())) {
//            throw new AuthException("账号密码错误!");
//        }


        AdminLoginVO adminLoginVO = new AdminLoginVO();
        BeanUtils.copyProperties(admin, adminLoginVO);
        String adminId = ConvertUtils.toStr(admin.getId());
        // 登录信息写到redis
        String token = JwtUtils.createToken(adminId, "admin");
        adminLoginVO.setToken(token);
        redisUtils.del(Constants.AUTH_ADMIN_KEY + adminId);
        redisUtils.set(Constants.AUTH_ADMIN_KEY + adminId, securitySystemAdminUserDetails, jwtConfig.getExpireTime());
        return adminLoginVO;
    }

    /**
     * 退出
     *
     * @return void
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/23 13:46:05
     */
    @Override
    public void logout() {
        SystemAdmin admin = getAdminInfo();
        // 删除redis
        redisUtils.del(Constants.AUTH_ADMIN_KEY + ConvertUtils.toStr(admin.getId()));
    }

    /**
     * 获取登录的用户信息
     *
     * @param
     * @return SystemAdmin
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/26 18:12:02
     */
    public SystemAdmin getAdminInfo() {
        SystemAdmin admin = null;
        // 获取SecurityContextHolder中的用户id
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication.getPrincipal() instanceof String) {
            // security的匿名处理 todo
        } else {
            admin = (SystemAdmin) authentication.getPrincipal();
        }
        return admin;
    }

    /**
     * 获取登录的用户信息
     *
     * @param
     * @return AdminVO
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/26 18:12:42
     */
    public AdminVO loginView() {
        SystemAdmin admin = getAdminInfo();
        return view(admin.getId());
    }

    /**
     * 修改当前用户资料
     *
     * @param adminDTO
     * @return Map<String, Object>
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/29 23:10
     */
    @Override
    public Map<String, Object> profile(AdminDTO adminDTO) {
        //登录用户信息
        SystemAdmin adminInfo = getAdminInfo();
        Assert.notNull(adminDTO, ResultCode.NOT_DATA_FOUNT);
        LambdaUpdateWrapper<SystemAdmin> objectUpdateWrapper = new LambdaUpdateWrapper<>();
        objectUpdateWrapper.eq(SystemAdmin::getId, adminInfo.getId());
        objectUpdateWrapper.set(SystemAdmin::getNickName, adminDTO.getNickName());
        objectUpdateWrapper.set(SystemAdmin::getPhonenumber, adminDTO.getPhonenumber());
        objectUpdateWrapper.set(SystemAdmin::getSex, adminDTO.getSex());
        int i = this.baseMapper.update(null, objectUpdateWrapper);
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", i);
        return map;
    }

    /**
     * 修改当前用户密码
     *
     * @param adminPwdDTO
     * @return Map<String, Object>
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/29 23:18
     */
    @Override
    public Map<String, Object> profileResetPwd(AdminPwdDTO adminPwdDTO) {
        //登录用户信息
        SystemAdmin adminInfo = getAdminInfo();
        if (adminInfo == null) {
            throw new BadException("请先登录！");
        }

        // ----通过secuity授权认证验证密码
        // 使用AuthenticationManager.authenticate进行用户认证 ---> 会去调用我们的SystemAdminDetailServiceImpl的loadUserByUsername进行认证
//        Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(adminInfo.getUserName(), adminPwdDTO.getOldPassword()));
//        // 如果认证没通过，给出提示
//        if (authenticate == null) {
//            throw new BadException("旧密码错误！");
//        }
        if (!validatePassword(adminPwdDTO.getOldPassword(), adminInfo.getPassword())) {
            throw new BadException("旧密码错误！");
        }

        LambdaUpdateWrapper<SystemAdmin> objectUpdateWrapper = new LambdaUpdateWrapper<>();
        objectUpdateWrapper.eq(SystemAdmin::getId, adminInfo.getId());
        objectUpdateWrapper.set(SystemAdmin::getPassword, generatePasswordHash(adminPwdDTO.getNewPassword()));
        int i = this.baseMapper.update(null, objectUpdateWrapper);
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", i);
        logout();
        return map;
    }

    /**
     * 修改当前用户头像
     *
     * @param imageUrl
     * @return Map<String, Object>
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/6/7 20:28
     */
    public Map<String, Object> profileAvatar(String imageUrl) {
        //登录用户信息
        SystemAdmin adminInfo = getAdminInfo();
        if (adminInfo == null) {
            throw new BadException("请先登录！");
        }
        LambdaUpdateWrapper<SystemAdmin> objectUpdateWrapper = new LambdaUpdateWrapper<>();
        objectUpdateWrapper.eq(SystemAdmin::getId, adminInfo.getId());
        objectUpdateWrapper.set(SystemAdmin::getAvatar, imageUrl);
        int i = this.baseMapper.update(null, objectUpdateWrapper);
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", i);
        map.put("avatar", imageUrl);
        return map;
    }

    /**
     * 创建
     *
     * @param adminDTO
     * @return Map<String, Object>
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/24 10:42:56
     */
    @Override
    @Transactional
    public Map<String, Object> create(AdminDTO adminDTO) {
        Long count = this.baseMapper.selectCount(new LambdaQueryWrapper<SystemAdmin>().eq(SystemAdmin::getUserName, adminDTO.getUserName()));
        if (count > 0) {
            throw new BadException("管理员账号已存在!");
        }
        // 加密密码
        adminDTO.setPassword(generatePasswordHash(adminDTO.getPassword()));
        Map<String, Object> map = new HashMap<>();
        SystemAdmin systemAdmin = new SystemAdmin();
        BeanUtils.copyProperties(adminDTO, systemAdmin);
        this.baseMapper.insert(systemAdmin);
        // 用户角色
        systemAdminRoleService.roles(systemAdmin.getId(), adminDTO.getRoles());
        // 部门
        systemAdminDeptService.depts(systemAdmin.getId(), adminDTO.getDepts());
        // 部门
        systemAdminStationService.stations(systemAdmin.getId(), adminDTO.getStations());
        map.put("id", systemAdmin.getId());
        return map;
    }

    /**
     * 详情
     *
     * @param id
     * @return AdminVO
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/24 10:45:27
     */
    @Override
    public AdminVO view(Long id) {
        SystemAdmin admin = this.baseMapper.selectById(id);
        Assert.notNull(admin, ResultCode.NOT_DATA_FOUNT);
        AdminVO adminVO = new AdminVO();
        BeanUtils.copyProperties(admin, adminVO);
        return adminVO;
    }

    /**
     * 修改
     *
     * @param id
     * @param adminDTO
     * @return Map<String, Object>
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/24 10:54:53
     */
    @Override
    @Transactional
    public Map<String, Object> update(Long id, AdminDTO adminDTO) {
        SystemAdmin systemAdmin = this.baseMapper.selectById(id);
        // 不存在
        Assert.notNull(systemAdmin, ResultCode.NOT_DATA_FOUNT);
        if (systemAdmin.getUserName().compareTo(adminDTO.getUserName()) != 0) {
            Long count = this.baseMapper.selectCount(new LambdaQueryWrapper<SystemAdmin>().eq(SystemAdmin::getUserName, adminDTO.getUserName()));
            if (count > 0) {
                throw new BadException("管理员账号已存在!");
            }
        }
        HashMap<String, Object> map = new HashMap<>();
        // 修改密码了
        if (StringUtils.isNotEmpty(adminDTO.getPassword())) {
            // 加密密码
            adminDTO.setPassword(generatePasswordHash(adminDTO.getPassword()));
        }
        BeanUtils.copyProperties(adminDTO, systemAdmin, "created_at");
        systemAdmin.setId(id);
        int i = this.baseMapper.updateById(systemAdmin);
        // 用户角色
        systemAdminRoleService.roles(systemAdmin.getId(), adminDTO.getRoles());
        // 部门
        systemAdminDeptService.depts(systemAdmin.getId(), adminDTO.getDepts());
        // 部门
        systemAdminStationService.stations(systemAdmin.getId(), adminDTO.getStations());

        map.put("result", i);
        return map;
    }

    /**
     * 删除
     *
     * @param ids
     * @return Map<String, Object>
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/24 10:59:04
     */
    @Override
    public Map<String, Object> delete(String ids) {
        int i = 0;
        if (ids.contains(",")) {
            i = this.baseMapper.delete(new LambdaQueryWrapper<SystemAdmin>().in(SystemAdmin::getId, ConvertUtils.toLongArray(ids)));
        } else {
            i = this.baseMapper.deleteById(ConvertUtils.toLong(ids));
        }
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", i);
        return map;
    }

    /**
     * 列表
     *
     * @param adminBO
     * @return IPage<AdminVO>
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/24 11:04:09
     */
    @Override
    public IPage<SystemAdmin> lists(AdminBO adminBO) {
        Page<SystemAdmin> page = this.baseMapper.selectPage(adminBO.toPage(), adminBO.toQueryWrapper());

        page.convert(s -> {
            AdminVO adminVO = new AdminVO();
            BeanUtils.copyProperties(s, adminVO);
            return adminVO;
        });
        return page;
    }

    /**
     * 权限
     *
     * @param admin_id
     * @return List<String>
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/16 11:39:51
     */
    @Override
    public List<String> getPermissionsByUserId(Long admin_id) {
        return this.baseMapper.getPermissionsByUserId(admin_id);
    }

    /**
     * 状态修改
     *
     * @param changeStatusDTO
     * @return Map<String, Object>
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/29 22:33
     */
    @Override
    public Map<String, Object> changeStatus(ChangeStatusDTO changeStatusDTO) {
        SystemAdmin systemAdmin = this.baseMapper.selectById(changeStatusDTO.getId());
        Assert.notNull(systemAdmin, ResultCode.NOT_DATA_FOUNT);
        LambdaUpdateWrapper<SystemAdmin> objectUpdateWrapper = new LambdaUpdateWrapper<>();
        objectUpdateWrapper.eq(SystemAdmin::getId, changeStatusDTO.getId());
        objectUpdateWrapper.set(SystemAdmin::getStatus, changeStatusDTO.getStatus());
        int i = this.baseMapper.update(null, objectUpdateWrapper);
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", i);
        return map;
    }

    /**
     * 密码修改
     *
     * @param adminDTO
     * @return Map<String, Object>
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/29 22:49
     */
    @Override
    public Map<String, Object> resetPwd(AdminDTO adminDTO) {
        SystemAdmin systemAdmin = this.baseMapper.selectById(adminDTO.getId());
        Assert.notNull(systemAdmin, ResultCode.NOT_DATA_FOUNT);
        LambdaUpdateWrapper<SystemAdmin> objectUpdateWrapper = new LambdaUpdateWrapper<>();
        objectUpdateWrapper.eq(SystemAdmin::getId, adminDTO.getId());
        objectUpdateWrapper.set(SystemAdmin::getPassword, generatePasswordHash(adminDTO.getPassword()));
        int i = this.baseMapper.update(null, objectUpdateWrapper);
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", i);
        return map;
    }

    /**
     * 生成密码
     * 对应php password_hash()
     * spring security也是这个加密方式。
     *
     * @param password
     * @return String
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/4/29 7:15
     */
    private String generatePasswordHash(String password) {
//        return BCrypt.with(BCrypt.Version.VERSION_2Y).hashToString(10, password.toCharArray());
        return passwordEncoder.encode(password);
    }

    /**
     * 验证密码
     * 对应php password_verify()
     *
     * @param password         真实密码
     * @param bcryptHashString 加密后字符
     * @return boolean
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/4/29 7:14
     */
    private boolean validatePassword(String password, String bcryptHashString) {
//        BCrypt.Result result = BCrypt.verifyer().verify(password.toCharArray(), bcryptHashString);
//        return result.verified;
        return passwordEncoder.matches(password, bcryptHashString);
    }
}




